/**********************************************************
 * File description:ProcedureService.cpp
 * Function:扩增程序服务类
 * Current Version: 1.0.0
 * Author: 夏新宇
 * Date: 2022-10-02
 *  -----
 *  Copyright by bokun Software.
 **********************************************************/

#include "ProcedureService.h"


/*!
 * \brief 构造函数
 *
 * \param
 *
 *
 * \return
 *
 * Detail here!!!
 */
ProcedureService::ProcedureService(){


    path = QCoreApplication::applicationDirPath();
    conv = new Convertion();
    cfs = new ConfigFileService();
    char * filePath;
    QByteArray tempByte = (path + "/" + CONFIG_FILE_NAME).toLocal8Bit();
    filePath = tempByte.data();
    sqlD = new SqliteDriver();
    sqlD->setDBDir(path);
    sqlD->openDB(DBNAME);
    //    simplexml = new Simplexml(filePath);
    //    simplexml->next("procedure");
}

/*!
 * \brief 析构函数
 *
 * \param
 *
 *
 * \return
 *
 * Detail here!!!
 */

ProcedureService::~ProcedureService(){
    sqlD->closeDB();
}



/*!
 * \brief 根据实验id获取实验运行时间
 *
 * \param QString experimentId 实验id
 *
 * \param
 *
 * \return 实验运行时间
 *
 * Detail here!!!
 */
int ProcedureService::getRunTime(QString experimentId){
    Convertion * conv = new Convertion();
    QList<QHash<QString,QString>> res;
    QList<QHash<QString,QString>> stageList;
    sqlD->queryExec(ALL_STAGE + experimentId,stageList);
    int runtime = 0;
    for(int i = 0;i < stageList.size();i ++){
        QString stage_id = stageList[i]["id"];
        QList<QHash<QString,QString>> stepList;
        int sum = 0;
        sqlD->queryExec(ALL_STEP + stage_id,stepList);
        for(int j = 0;j < stepList.size();j ++){
            sum += stepList[j]["duration"].toInt();
        }
        runtime += stageList[i]["name"] == PCR_STAGE ? sum * stageList[i]["cycle_number"].toInt() : sum;
    }
    return runtime;
}

/*!
 * \brief 根据类型创建默认扩增程序
 *
 * \param QString experimentId 实验id
 *
 * \param QString experimentType 实验类型
 *
 * \return QList<QString> ids 包含生成的阶段id的QList数组
 *
 * Detail here!!!
 */

QList<QString> ProcedureService::getProcedureByType(QString experimentId, QString experimentType){
    QList<QString> ids;
    QList<QString> stageName = cfs->getStageNameByExperimentType(experimentType);
    LOG << "ProcedureService stageName, expType" << stageName << experimentType;
    for(int i = 0;i < stageName.size();i ++){
        ProcedureStage stage;
        stage.name = stageName[i];
        stage.cycle_number = cfs->getStageByStageName(stage.name).cycle_number;
        stage.experiment_id = experimentId;
        stage.stage_order = QString::number(i + 1);
        sqlD->addData(PROCEDURE_STAGE_TABLE,stage.structToQHash()); //在扩增程序阶段表中新增指定id的阶段
        QList<QHash<QString,QString>> res;
        sqlD->queryExec(conv->stringToQString(LATEST_STAGE_ID),res);
        QString stage_id = res[0]["id"];
        QList<ProcedureStep> steps = cfs->getStepByStageName(stage.name);
        for(int j = 0;j < steps.size(); j ++){
            steps[j].stage_id = stage_id;
            steps[j].step_order = QString::number(j + 1);
            sqlD->addData(PROCEDURE_STEP_TABLE,steps[j].structToQHash());
        }
        ids.append(stage_id);
    }
    return ids;
}


/*!
 * \brief 插入阶段(支持重复插入阶段！！！)
 *
 * \param QString experimentId 实验id
 * \param QString stageName 插入阶段名称
 * \param QString stageOrder 插入阶段次序
 *
 * \return int id 生成阶段的id
 *
 * Detail here!!!
 */
QString ProcedureService::insertStage(QString experimentId,QString stageName,QString stageOrder){
    QString id;

    ProcedureStage stage = cfs->getStageByStageName(stageName);
    sqlD->queryExec(INSERT_STAGE1 + stageOrder + " AND experiment_id = " //将后续阶段次序加1
                    + experimentId);
    stage.stage_order = stageOrder;
    stage.experiment_id = experimentId;
    stage.name = stageName;

    sqlD->addData(PROCEDURE_STAGE_TABLE,stage.structToQHash());
    QList<QHash<QString,QString>> res;
    sqlD->queryExec(conv->stringToQString(LATEST_STAGE_ID),res);
    QString stage_id = res[0]["id"];

    id = stage_id;

    QList<ProcedureStep> steps = cfs->getStepByStageName(stage.name);
    for(int j = 0;j < steps.size(); j ++){
        steps[j].stage_id = stage_id;
        steps[j].step_order = QString::number(j + 1);
        sqlD->addData(PROCEDURE_STEP_TABLE,steps[j].structToQHash());
    }
    return id;
}

/*!
 * \brief 插入阶段(支持重复插入阶段！！！)
 *
 * \param QString experimentId 实验id
 * \param QString stageName 插入阶段名称
 * \param QString stageOrder 插入阶段次序
 * \param QString cycleNumber 循环次数
 * \param QList<QHash<QString,QString>> stepList 当前阶段下的步骤数据
 *
 * \return int id 生成阶段的id
 *
 * Detail here!!!
 */
QString ProcedureService::insertStage(QString experimentId,QString stageName,QString stageOrder,QString cycleNumber,QList<QHash<QString,QString>> stepList){
    QString id;

    ProcedureStage stage = cfs->getStageByStageName(stageName);

    stage.stage_order = stageOrder;
    stage.experiment_id = experimentId;
    stage.name = stageName;
    stage.cycle_number = cycleNumber;

    sqlD->addData(PROCEDURE_STAGE_TABLE,stage.structToQHash());
    QList<QHash<QString,QString>> res;
    sqlD->queryExec(conv->stringToQString(LATEST_STAGE_ID),res);
    QString stage_id = res[0]["id"];

    id = stage_id;

    for(int i = 0;i < stepList.size();i ++){
        for(int j = i + 1;j < stepList.size();j ++){
            int k = stepList[i]["step_order"].toInt();
            int l = stepList[j]["step_order"].toInt();
            if(k > l){
                QHash<QString,QString> thash = stepList[i];
                stepList[i] = stepList[j];
                stepList[j] = thash;
            }
        }
    }

    for(int i = 0;i < stepList.size();i ++){
        ProcedureStep step;
        step.stage_id = stage_id;
        step.step_order = QString::number(i + 1);
        step.temperature = stepList[i]["temperature"];
        step.duration = stepList[i]["duration"];
        step.rate = stepList[i]["rate"];
        step.is_fluorescence = stepList[i]["is_fluorescence"];
        sqlD->addData(PROCEDURE_STEP_TABLE,step.structToQHash());
    }

    return id;
}

/*!
 * \brief 删除阶段
 *
 * \param QString experimentId 实验id
 * \param QString stageId 删除阶段id
 * \param QString stageOrder 阶段次序
 *
 * \return
 *
 * Detail here!!!
 */
void ProcedureService::deleteStage(QString experimentId,QString stageId,QString stageOrder){

    sqlD->delData(PROCEDURE_STAGE_TABLE,"WHERE id = " + stageId); //删除阶段表中数据

    sqlD->queryExec(DELETE_STAGE1 + stageOrder + " AND experiment_id = " //将后续阶段次序减1
                    + experimentId);

    sqlD->delData(PROCEDURE_STEP_TABLE,"WHERE stage_id = " + stageId); //删除步骤表中数据
}

/*!
 * \brief 插入步骤
 *
 * \param ProcedureStep step 包含插入步骤信息的结构体
 *
 * \return
 *
 * Detail here!!!
 */
void ProcedureService::insertStep( ProcedureStep step){

    sqlD->queryExec(INSERT_STEP1 + step.step_order + " AND stage_id = " //将后续步骤次序加1
                    + step.stage_id);

    sqlD->addData(PROCEDURE_STEP_TABLE,step.structToQHash()); //将新步骤插入

}



/*!
 * \brief 删除步骤
 *
 * \param QString stageId 插入阶段Id
 * \param QString stepOrder 插入步骤在所属阶段中的位置
 *
 * \return
 *
 * Detail here!!!
 */
void ProcedureService::deleteStep(QString stageId,QString stepOrder){
    sqlD->delData(PROCEDURE_STEP_TABLE,"WHERE stage_id = " + stageId + " AND step_order = " //删除指定步骤
                  + stepOrder);

    sqlD->queryExec(DELETE_STEP1 + stepOrder + " AND stage_id = " //将后续步骤次序减1
                    + stageId);


    QList<QHash<QString,QString>> res;
    sqlD->queryExec(conv->stringToQString(COUNT_STEPS) + stageId,res);
    QString cnt = res[0]["cnt"];
    if(cnt == "0")
        sqlD->delData(PROCEDURE_STAGE_TABLE,"WHERE id = " + stageId); //删除阶段表中数据
}



/*!
 * \brief 修改循环(只有PCR阶段才可以插入，前端需要校验！！！)
 *
 * \param QString stageId 阶段
 * \param QString cycleNumber 循环次数
 *
 *
 * \return
 *
 * Detail here!!!
 */
void ProcedureService::insertCycle(QString stageId,QString cycleNumber){
    QHash<QString,QString> data;
    data["cycle_number"] = cycleNumber;
    sqlD->updateData(PROCEDURE_STAGE_TABLE,data,"WHERE id = " + stageId);
}


/*!
 * \brief 给指定步骤开启荧光采集
 *
 * \param QString stageId 阶段Id
 * \param QString stepOrder 步骤次序
 *
 * \return
 *
 * Detail here!!!
 */
void ProcedureService::openFluorescence(QString stageId,QString stepOrder,bool isOpen){
    QHash<QString,QString> data;
    if(isOpen)
        data["is_fluorescence"] = "1";
    else data["is_fluorescence"] = "0";
    sqlD->updateData(PROCEDURE_STEP_TABLE,data,"WHERE stage_id = " + stageId + " AND step_order = "
                     + stepOrder);
}

/*!
 * \brief 修改指定步骤持续时间
 *
 * \param QString stageId 阶段Id
 * \param QString stepOrder 步骤次序
 *
 * \return
 *
 * Detail here!!!
 */
void ProcedureService::updateDuration(QString stageId, QString stepOrder, QString duration){
    QHash<QString,QString> data;
    data["duration"] = duration;
    sqlD->updateData(PROCEDURE_STEP_TABLE,data,"WHERE stage_id = " + stageId + " AND step_order = "
                     + stepOrder);
}

/*!
 * \brief 修改指定步骤温度
 *
 * \param QString stageId 阶段Id
 * \param QString stepOrder 步骤次序
 *
 * \return
 *
 * Detail here!!!
 */
void ProcedureService::updateTemperature(QString stageId, QString stepOrder, QString temperature){
    QHash<QString,QString> data;
    data["temperature"] = temperature;
    sqlD->updateData(PROCEDURE_STEP_TABLE,data,"WHERE stage_id = " + stageId + " AND step_order = "
                     + stepOrder);
}

/*!
 * \brief 修改指定步骤升温速率
 *
 * \param QString stageId 阶段Id
 * \param QString stepOrder 步骤次序
 *
 * \return
 *
 * Detail here!!!
 */
void ProcedureService::updateRate(QString stageId, QString stepOrder, QString rate){
    QHash<QString,QString> data;

    qDebug() << "datarate" << data;

    data["rate"] = rate;
    sqlD->updateData(PROCEDURE_STEP_TABLE,data,"WHERE stage_id = " + stageId + " AND step_order = "
                     + stepOrder);
}

/*!
 * \brief 恢复扩增程序
 *
 * \param QString experimentId 实验id
 * \param QString experimentType 实验类型
 *
 * \return
 *
 * Detail here!!!
 */
void ProcedureService::restoreDefaultState(QString experimentId, QString experimentType){
    QList<QHash<QString,QString>> stageList;
    sqlD->getData(PROCEDURE_STAGE_TABLE,stageList,"WHERE experiment_id = " + experimentId);
    sqlD->delData(PROCEDURE_STAGE_TABLE,"WHERE experiment_id = " + experimentId);
    for(int i = 0;i < stageList.size();i ++){
        sqlD->delData(PROCEDURE_STEP_TABLE,"WHERE stage_id = " + stageList[i]["id"]);
    }
    getProcedureByType(experimentId,experimentType);
}



/*!
 * \brief 查询实验扩增程序的所有步骤
 *
 * \param QString experimentId 实验id
 *
 *
 *
 * \return
 *
 * Detail here!!!
 */

QHash<QHash<QString,QString>,QList<QHash<QString,QString>>> ProcedureService::getProcedureData(QString experimentId){
    QHash<QHash<QString,QString>,QList<QHash<QString,QString>>> res;
    QList<QHash<QString,QString>> stageList;
    sqlD->getData(PROCEDURE_STAGE_TABLE,stageList,"WHERE experiment_id = " + experimentId + " ORDER BY stage_order");
    for(int i = 0;i < stageList.size();i ++){
        QString stageId = stageList[i]["id"];
        QList<QHash<QString,QString>> stepList;
        sqlD->getData(PROCEDURE_STEP_TABLE,stepList,"WHERE stage_id = " + stageId + " ORDER BY step_order");
        res[stageList[i]] = stepList;
    }
    return res;
}

/*!
 * \brief 删除指定实验的扩增程序数据
 *
 * \param QString experimentId 实验id
 *
 *
 *
 * \return
 *
 * Detail here!!!
 */
void ProcedureService::deleteProcedureData(QString experimentId){
    QList<QHash<QString,QString>> stageList;
    sqlD->getData(PROCEDURE_STAGE_TABLE,stageList,"WHERE experiment_id = " + experimentId + " ORDER BY stage_order");
    sqlD->delData(PROCEDURE_STAGE_TABLE,"WHERE experiment_id = " + experimentId);
    for(int i = 0;i < stageList.size();i ++){
        sqlD->delData(PROCEDURE_STEP_TABLE,"WHERE stage_id = " + stageList[i]["id"]);
    }
}

/*!
 * \brief 通过温度回传数据获取指定步骤时间和温度
 * \param QString experimentId 实验Id
 * \param QString jieduan 回传阶段
 * \param QString jieduan 回传编号
 * \param QString buzhou 回传步骤
 *
 * \return
 *
 * Detail here!!!
 */
QList<float> ProcedureService::getStepTimeAndTemperatureByRecieve(QString experimentId,QString jieduan, QString bianhao, QString buzhou){

    qDebug() << "procedure service QString experimentId,QString jieduan, QString bianhao, QString buzhou" << experimentId << jieduan << bianhao << buzhou;

    QHash<QHash<QString,QString>,QList<QHash<QString,QString>>> res = getProcedureData(experimentId);

    qDebug() << "procedureService procedureList" << res;

    QList<QHash<QString,QString>> stage1;
    QList<QHash<QString,QString>> stage2;
    QList<QHash<QString,QString>> stage3;

    QHash<QHash<QString,QString>,QList<QHash<QString,QString>>>::const_iterator it;

    for (it=res.begin(); it!=res.end();++it)
    {
        if(it.key()["name"] == HOLDING_STAGE)
        {
            stage1.append(it.key());
        }
        else if (it.key()["name"] == PCR_STAGE)
        {
            stage2.append(it.key());
        }
        else if (it.key()["name"] == DISSOLUTION_CURVE_STAGE)
        {
            stage3.append(it.key());
        }
    }


    for(int i = 0;i < stage1.size();i ++){
        for(int j = i + 1;j < stage1.size();j ++){
            int k = stage1[i]["stage_order"].toInt();
            int l = stage1[j]["stage_order"].toInt();
            if(k > l){
                QHash<QString,QString> t = stage1[i];
                stage1[i] = stage1[j];
                stage1[j] = t;
            }
        }
    }

    for(int i = 0;i < stage2.size();i ++){
        for(int j = i + 1;j < stage2.size();j ++){
            int k = stage2[i]["stage_order"].toInt();
            int l = stage2[j]["stage_order"].toInt();
            if(k > l){
                QHash<QString,QString> t = stage2[i];
                stage2[i] = stage2[j];
                stage2[j] = t;
            }
        }
    }

    for(int i = 0;i < stage3.size();i ++){
        for(int j = i + 1;j < stage3.size();j ++){
            int k = stage3[i]["stage_order"].toInt();
            int l = stage3[j]["stage_order"].toInt();
            if(k > l){
                QHash<QString,QString> t = stage3[i];
                stage3[i] = stage3[j];
                stage3[j] = t;
            }
        }
    }

    bool ok1, ok2, ok3;
    qDebug() << "stage1.size()" << stage1.size() << "bianhao.toInt(&ok,16)" << bianhao.toInt(&ok1, 16);
    qDebug() << "stage2.size()" << stage2.size() << "jieduan.toInt(&ok,16)" << jieduan.toInt(&ok2, 16);
    qDebug() << "stage3.size()" << stage3.size() << "buzhou.toInt(&ok,16)" << buzhou.toInt(&ok3, 16);
    qDebug() << "ok1" << ok1 << "ok2" << ok2 << "ok3" << ok3;

    try {
        bool ok;
        QList<float> result;
        QHash<QString,QString> stage;
        if(jieduan.toInt(&ok,16) == 1){
            ok = false;
            if(stage1.size() < bianhao.toInt(&ok,16)) return result;
            stage = stage1[bianhao.toInt(&ok,16) - 1];
        }else if(jieduan.toInt(&ok,16) == 2){
            ok = false;
            if(stage2.size() < bianhao.toInt(&ok,16)) return result;
            stage = stage2[bianhao.toInt(&ok,16) - 1];
        }else{
            if(stage3.size() < bianhao.toInt(&ok,16)) return result;
            stage = stage3[bianhao.toInt(&ok,16) - 1];
        }
        ok = false;

        qDebug() << "stageString" << stage;

        QList<QHash<QString,QString>> steps = res[stage];

        qDebug() << "stepsList" << steps;

        if(steps.size() < buzhou.toInt(&ok,16)) return result;
        result.append(steps[buzhou.toInt(&ok,16) - 1]["duration"].toFloat());
        result.append(steps[buzhou.toInt(&ok,16) - 1]["temperature"].toFloat());

        qDebug() << "result" << steps[buzhou.toInt(&ok,16) - 1]["temperature"] << steps[buzhou.toInt(&ok,16) - 1]["duration"];
        qDebug() << "result" << result;

        return result;
    } catch(...) {
        qDebug() << "回传数据有误，指定步骤不存在" << endl;
    }

}
